package com.xuanyue.db.xuan.core.index;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map.Entry;

import com.xuanyue.db.xuan.core.exception.IndexException;
import com.xuanyue.db.xuan.core.exception.SQLException;
import com.xuanyue.db.xuan.core.table.IBitIndex;
import com.xuanyue.db.xuan.core.table.IColumn;
import com.xuanyue.db.xuan.core.table.INumberColumn;
import com.xuanyue.db.xuan.core.table.ISortElement;
import com.xuanyue.db.xuan.core.table.expr.Expr;
import com.xuanyue.db.xuan.core.table.expr.ExprValue;
import com.xuanyue.db.xuan.core.table.expr.ExprValueType;
import com.xuanyue.db.xuan.core.table.expr.SortTemplate;
import com.xuanyue.db.xuan.core.table.sort.SortElement;
import com.xuanyue.db.xuan.msg.VLAUETYPE;
/**
 * 无符号整形
 * @author 解观海
 * @email  guanhaixie@sina.cn
 * @date 2020年6月23日
 * @version 0.1
 */
public class UNumberIndex implements INumberColumn<Long>{
	private HashMap<Integer,IBitIndex> positionV2bit = new HashMap<>();
	private BitIndex mask = new BitIndex();
	private int maxBit=44;
	private String path;
	
	
	
	public UNumberIndex(int max) {
		super();
		this.maxBit = max;
		for(int i=0;i<maxBit;i++) {
			positionV2bit.put(i, new BitIndex());
		}
	}
	public UNumberIndex() {
		this(44);
	}
	
	public long max() {
		BitIndex now = new BitIndex();
		now.flush(mask.getMaxId());
		now.copyFrom(mask);
		IBitIndex bi = null;
		for(int i=maxBit-1;i>-1;i--) {
			bi=positionV2bit.get(i);
			int c1 = now.andByCardinality(bi);
			if(c1>0) {
				now.and(bi);
			}else {
				now.andNot(bi);
			}
		}
		
		int mid = now.nextSetBit(0);
		if(mid>-1) {
			return this.get(mid);
		}else {
			return 0;
		}
	}
	
	
	@Override
	public boolean modify(IColumn newOne,boolean cpDataFromOld) {
		UNumberIndex ui = (UNumberIndex)newOne;
		if(this.maxBit>ui.maxBit) {//缩小
			for(int i=ui.maxBit;i<this.maxBit;i++) {
				new File(String.format("%s/positionV2bit/%s", path,i)).delete();
			}
		}else  if(this.maxBit<ui.maxBit){//放大
			BitIndex bi = null;
			for(int i=this.maxBit;i<ui.maxBit;i++) {
				bi = new BitIndex();
				positionV2bit.put(i, new BitIndex());
				bi.setPath(String.format("%s/positionV2bit/%s", path,i));
				bi.init();
				bi.flush(mask.getMaxId());
				bi.save();
			}
		}
		if(!cpDataFromOld) {
			this.mask.setAll(false);
		}
		return true;
	}
	
	private void reduce(long derta) {
		BitIndex before = new BitIndex();
		before.flush( ((BitIndex)mask).getMaxId() );
		before.setAll(false);
		
		BitIndex cache = new BitIndex();
		cache.flush( ((BitIndex)mask).getMaxId() );
		cache.setAll(false);
		
		BitIndex cache2 = new BitIndex();
		cache2.flush( ((BitIndex)mask).getMaxId() );
		
		IBitIndex now = positionV2bit.get(0);
		if( (derta&1l)==1l ) {
			cache.copyFrom(now);//进1
			now.not();
			cache.not();
		}
		
		for(int i=1;i<maxBit;i++) {
			now = positionV2bit.get(i);
			if( (derta&(1l<<i))==(1l<<i)  ) {
				//当前位减1
				//（当前位为0 且 前一位不借位）    或 （ 当前位为1  且前一位借位）  -->>  亦或 然后取反
				cache2.copyFrom(now);
				now.xor( cache );
				now.not();
				
				//（当前位为0） 或 （ 当前位为1 且前一位借位） -->  对 (当前位为1 且前一位不借位) 取反
				cache2.andNot(cache);
				cache2.not();
				cache.copyFrom(cache2);
			}else {
				//当前位减0
				// （当前位为1 且 前一位不借位  ） 或  （当前位为0 且 前一位借位） -->> 亦或
				cache2.copyFrom(now);
				now.xor(cache);
				// 当前位为0 且 前一位借位
				cache.andNot( cache2 );
			}
		}
	}
	
	private void add(long derta) {
		BitIndex before = new BitIndex();
		before.flush( ((BitIndex)mask).getMaxId() );
		before.setAll(false);
		
		BitIndex cache = new BitIndex();
		cache.flush( ((BitIndex)mask).getMaxId() );
		cache.setAll(false);
		
		IBitIndex now = positionV2bit.get(0);
		if( (derta&1l)==1l ) {
			cache.copyFrom(now);//进1
			now.not();
		}
		BitIndex cache2 = new BitIndex();
		cache2.flush( ((BitIndex)mask).getMaxId() );
		
		for(int i=1;i<maxBit;i++) {
			now = positionV2bit.get(i);
			if( (derta&(1l<<i))==(1l<<i)  ) {
				//derta 的当前bit位是1
				//
				cache2.copyFrom(now);//保留当前值
				now.and(cache);//三个1  当前为1
				cache.or(cache2);//前一位进一位或当前为为1，则向前进位。
			}else {
				//derta 的当前bit位是0
				cache2.copyFrom(now);
				now.xor(cache);//当前位 和 前一位的进位 不同(亦或) 则当前位为1
				cache.and(cache2);//当前位 和 前一位的进位  同时为1 则进位
			}
		}
	}
	
	public void derta(long derta) {
		if(derta==0) {
			return;
		}else if(derta>0) {
			add(derta);
		}else {
			reduce(derta);
		}
	}
	
	public int getSize() {
		return maxBit;
	}
	
	@Override
	public void setPath(String path) {
		this.path = path;
		mask.setPath(String.format("%s/mask", path));
		for(Entry<Integer,IBitIndex> en:positionV2bit.entrySet()) {
			en.getValue().setPath(String.format("%s/positionV2bit/%s", path,en.getKey()));
		}
	}
	public IBitIndex getMask() {
		return mask;
	}
	
	public void flush(int maxId) {
		mask.flush(maxId);
		positionV2bit.forEach((k,v)->{
			v.flush(maxId);
		});
	}
	
	@Override
	public void save() {
		File p = new File(path);
		if(!p.exists()) {
			p.mkdirs();
		}
		mask.save();
		for(Entry<Integer,IBitIndex> en:positionV2bit.entrySet()) {
			en.getValue().save();
		} 
	}
	@Override
	public void load() {
		File p = new File(path);
		if(!p.exists()) {
			throw new IndexException(String.format("%s is not exists", path));
		}
		try {
			//mask = new BitIndex();
			mask.load();
			File pvs = new File(String.format("%s/positionV2bit", path));
			IBitIndex posi = null;
			positionV2bit.clear();
			for(File pv:pvs.listFiles()) {
				posi = new BitIndex();
				posi.setPath(String.format("%s/positionV2bit/%s",path, pv.getName()));
				posi.load();
				positionV2bit.put(Integer.parseInt(pv.getName()), posi);
			}
			this.maxBit = positionV2bit.size();
		} catch (Exception e) {
			throw new SQLException(e);
		}
	}
	@Override
	public void init() {
		File p = new File(path);
		if(!p.exists()) {
			p.mkdirs();
		}
		mask.init();
		File pvs = new File(String.format("%s/positionV2bit", path));
		pvs.mkdirs();
		for(Entry<Integer,IBitIndex> en:positionV2bit.entrySet()) {
			en.getValue().init();
		}
	}
	@Override
	public void equeals(IBitIndex cache,Number v) {
		if(v==null) {
			cache.copyFrom(mask);
			cache.not();
			return;
		}
		long value = v.longValue();
		cache.setAll(true);
		long t = 0;
		for(int i=0;i<this.maxBit;i++) {
			t=(1l<<i);
			if((value&t)==t) {
				cache.and(positionV2bit.get(i));
			}else {
				cache.andNot(positionV2bit.get(i));
			}
		}
		cache.and(mask);
	}
	
	private void handleGreater(IBitIndex cache,IBitIndex now, long value) {
		long t = 0;
		now.setAll(true);
		cache.setAll(false);
		for(int i=this.maxBit-1;i>=0;i--) {
			t=(1l<<i);
			if((value&t)==t) {
				now.and(positionV2bit.get(i));
			}else {
				cache.orByinnerAnd(now, positionV2bit.get(i));
				now.andNot( positionV2bit.get(i) );
			}
		}
	}
	
	@Override
	public void greater(IBitIndex cache,IBitIndex now, Number v) {
		handleGreater(cache, now, v.longValue());
		cache.and(mask);
	}

	private void handleLess(IBitIndex cache, IBitIndex now, long value) {
		long t;
		now.setAll(true);
		cache.setAll(false);
		for(int i=this.maxBit-1;i>=0;i--) {
			t=(1l<<i);
			if((value&t)==t) {
				cache.orByinnerAndNot(now, positionV2bit.get(i));
				now.and(positionV2bit.get(i));
			}else {
				now.andNot( positionV2bit.get(i) );
			}
		}
	}
	
	@Override
	public void less(IBitIndex cache, IBitIndex now, Number value) {
		handleLess(cache, now, value.longValue());
		cache.and(mask);
	}

	@Override
	public void greaterAndEq(IBitIndex cache, IBitIndex now, Number value) {
		this.handleGreater(cache, now, value.longValue());
		cache.or(now);
		cache.and(mask);
	}

	@Override
	public void lessAndEq(IBitIndex cache, IBitIndex now,Number value) {
		this.handleLess(cache, now, value.longValue());
		cache.or(now);
		cache.and(mask);
	}

	@Override
	public void set(int rowId,ExprValue valueO) {
		long value;
		if(ExprValueType.LONG.is(valueO.getType())) {
			value = valueO.getValue();
		}else if(ExprValueType.INT.is(valueO.getType())) {
			int v = valueO.getValue();
			value = v;
		}else if(ExprValueType.NULL.is(valueO.getType())) {
			mask.set(rowId, false);
			return;
		}else {
			throw new SQLException("value type ERROR:"+valueO.getType()+"  is not a int or long value");
		}
		long t = 0;
		for(int i=0;i<this.maxBit;i++) {
			t=(1l<<i);
			positionV2bit.get(i).set(rowId,((value&t)==t));
		}
		mask.set(rowId);
	}

	@Override
	public Long get(int rowId) {
		if(mask.get(rowId)) {
			long v = 0;
			for(int i=0;i<maxBit;i++) {
				if(positionV2bit.get(i).get(rowId)) {
					v +=(1l<<i);
				}
			}
			return v;
		}else {
			return null;
		}
		
	}
	@Override
	public ExprValue getExprValue(int rowId) {
		return new ExprValue(ExprValueType.LONG, get(rowId));
	}

	public int checkExpr(String method,Object value) {
		if(!(value instanceof Integer) &&!(value instanceof Long) ){
			return 0;
		}
		if("!=".equals(method)) {
			return 1;
		}else if("=".equals(method)) {
			return 1;
		}else if(">=".equals(method)){
			return 2;
		}else if(">".equals(method)){
			return 2;
		}else if("<=".equals(method)){
			return 2;
		}else if("<".equals(method)){
			return 2;
		}else {
			return 0;
			//throw new IndexException(  this.getClass().getName()+ " not support " + method);
		}
	}
	
	@Override
	public int getDataSize() {
		return positionV2bit.size()+1;
	}
	@Override
	public List<ISortElement> getSortE(SortTemplate sortTemplate) {
		List<ISortElement> r = new ArrayList<>();
		for(int i=maxBit-1;i>-1;i--) {
			SortElement e = new SortElement();
			e.setData(positionV2bit.get(i));
			e.setDesc(sortTemplate.isDesc());
			r.add(e);
		}
		return r;
	}
	@Override
	public boolean checkSortE(SortTemplate sortTemplate) {
		return sortTemplate.isType(SortTemplate.GENERAL);
	}
	@Override
	public VLAUETYPE getType() {
		return VLAUETYPE.LONG;
	}
	@Override
	public void saveRow(int rowId) {
		mask.saveRow(rowId);
		for(Entry<Integer,IBitIndex> en:positionV2bit.entrySet()) {
			en.getValue().saveRow(rowId);
		}
	}
	@Override
	public boolean checkType(ExprValue v) {
		return ExprValueType.INT.is(v.getType())||ExprValueType.LONG.is(v.getType())||ExprValueType.NULL.is(v.getType());
	}
	@Override
	public void exe(Expr expr, List<IBitIndex> caches) {
		ExprValue ev = expr.getValue();
		if(ExprValueType.LONG.is(ev.getType())||ExprValueType.INT.is(ev.getType())) {
			long value = 0;
			if(ExprValueType.INT.is(ev.getType())) {
				value = (int)ev.getValue();
			}else {
				value = ev.getValue();
			}
			if("=".equals(expr.getOp())) {
				this.equeals(caches.get(0), value);
				return;
			}else if("!=".equals(expr.getOp())) {
				this.equeals(caches.get(0), value);
				caches.get(0).not();
				return;
			}else if(">".equals(expr.getOp())) {
				this.greater(caches.get(0), caches.get(1), value);
				return;
			}else if(">=".equals(expr.getOp())) {
				this.greaterAndEq(caches.get(0), caches.get(1), value);
				return;
			}else  if("<".equals(expr.getOp())) {
				this.less(caches.get(0), caches.get(1), value);
				return;
			}else if("<=".equals(expr.getOp())) {
				this.lessAndEq(caches.get(0), caches.get(1), value);
				return;
			}else {
				throw new SQLException("expr right value is null but op is not '=' , '!=', '>', '>=', '<' or '<=' ");
			}
		}else if(ExprValueType.NULL.is(ev.getType())) {
			if("=".equals(expr.getOp())) {
				caches.get(0).copyFrom(mask);
				caches.get(0).not();
				return;
			}else if("!=".equals(expr.getOp())){
				caches.get(0).copyFrom(mask);
				return;
			}else {
				throw new SQLException("expr right value is null but op is not '=' or '!=' ");
			}
		}else {
			throw new SQLException("expr right value type error:"+expr.getCname()+"  ,it is "+ev.getType());
		}
		
	}
	@Override
	public int checkAndContSource(Expr expr) {
		ExprValue ev = expr.getValue();
		if(ExprValueType.LONG.is(ev.getType())||ExprValueType.INT.is(ev.getType())) {
			long x = 0;//
			if(ExprValueType.INT.is(ev.getType())) {
				x = (int)ev.getValue();
			}else {
				x=ev.getValue();
			}
			
			if(x<=0) {
				return 0;
			}
			if("=".equals(expr.getOp())||"!=".equals(expr.getOp())) {
				return 1;
			}else if(">".equals(expr.getOp())||">=".equals(expr.getOp())||
					"<".equals(expr.getOp())||"<=".equals(expr.getOp()))	{
				return 2;
			}else {
				return 0;
			}
		}else if(ExprValueType.NULL.is(ev.getType())) {
			if("=".equals(expr.getOp())||"!=".equals(expr.getOp())) {
				return 1;
			}else {
				return 0;
			}
		}else {
			return 0;
		}
		
	}
}
